﻿using System;
using System.Transactions;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using O1.Kernel;
using O1.Kernel.Storage.Transactions;

namespace Tests.O1.Kernel.Storage.Transactions
{
    [TestClass]
    public class ConcurrencyTokenTests
    {
        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenNotifyValueWrittenCallbackCallsBackWithExpectedId()
        {
            var token = new ConcurrencyToken(8776, 8770);
            long id = 0;
            token.ValueWrittenCallback += (o, a) => { id = a; };
            token.NotifyValueWritten(10);
            id.Should().Be(10);
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenNotifyValueWrittenDoesNotCallbackWhenHandlerRemoved()
        {
            var token = new ConcurrencyToken(8776, 8770);
            long id = 0;

            EventHandler<long> handler = (o, a) => { id = a; };
            token.ValueWrittenCallback += handler;
            token.ValueWrittenCallback -= handler;
            token.NotifyValueWritten(10);
            id.Should().Be(0);
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInvalidateMethodInvalidatesTokenAsExpected()
        {
            var token = new ConcurrencyToken(8776, 8770);
            token.Invalidate();
            token.Invalid.Should().BeTrue();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInitializesWithExpectedValues()
        {
            var token = new ConcurrencyToken(8776, 8770);
            token.TransactionId.Should().Be(8776);
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInScopeReturnsTrueWhenTxIDMatchesOuterScope()
        {
            var token1 = new ConcurrencyToken(8776, 8770);
            var token2 = new ConcurrencyToken(8777, 8770, token1);
            token2.InScope(new Tx(8776, ulong.MaxValue)).Should().BeTrue();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInScopeReturnsTrueWhenTxIDMatchesSelfWithOuterScope()
        {
            var token1 = new ConcurrencyToken(8776, 8770);
            var token2 = new ConcurrencyToken(8777, 8770, token1);
            token2.InScope(new Tx(8777, ulong.MaxValue)).Should().BeTrue();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInScopeReturnsTrueWhenTxIDMatchesSelfWithoutOuterScope()
        {
            var token = new ConcurrencyToken(8777, 8770);
            token.InScope(new Tx(8777, ulong.MaxValue)).Should().BeTrue();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInScopeReturnsFalseWhenTxIDDoesNotMatchSelf()
        {
            var token = new ConcurrencyToken(8777, 8770);
            token.InScope(new Tx(8778, ulong.MaxValue)).Should().BeFalse();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInScopeReturnsFalseWhenTxIDDoesNotMatchSelfOrOuterScope()
        {
            var token1 = new ConcurrencyToken(8776, 8770);
            var token2 = new ConcurrencyToken(8777, 8770, token1);
            token2.InScope(new Tx(8778, ulong.MaxValue)).Should().BeFalse();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInScopeReturnsTrueWhenTxIDMatchesOuterScopeForDeleteTx()
        {
            var addedTx = ((ulong)45).EmbedStatus(TxStatus.Committed);
            var token1 = new ConcurrencyToken(8776, 8770);
            var token2 = new ConcurrencyToken(8777, 8770, token1);
            token2.InScope(new Tx(addedTx, 8776)).Should().BeTrue();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInScopeReturnsTrueWhenTxIDMatchesSelfWithOuterScopeForDeleteTx()
        {
            var addedTx = ((ulong)45).EmbedStatus(TxStatus.Committed);
            var token1 = new ConcurrencyToken(8776, 8770);
            var token2 = new ConcurrencyToken(8777, 8770, token1);
            token2.InScope(new Tx(addedTx, 8777)).Should().BeTrue();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInScopeReturnsTrueWhenTxIDMatchesSelfWithoutOuterScopeForDeleteTx()
        {
            var addedTx = ((ulong)45).EmbedStatus(TxStatus.Committed);
            var token = new ConcurrencyToken(8777, 8770);
            token.InScope(new Tx(addedTx, 8777)).Should().BeTrue();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInScopeReturnsFalseWhenTxIDDoesNotMatchSelfForDeleteTx()
        {
            var addedTx = ((ulong)45).EmbedStatus(TxStatus.Committed);
            var token = new ConcurrencyToken(8777, 8770);
            token.InScope(new Tx(addedTx, 8778)).Should().BeFalse();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenInScopeReturnsFalseWhenTxIDDoesNotMatchSelfOrOuterScopeForDeleteTx()
        {
            var addedTx = ((ulong)45).EmbedStatus(TxStatus.Committed);
            var token1 = new ConcurrencyToken(8776, 8770);
            var token2 = new ConcurrencyToken(8777, 8770, token1);
            token2.InScope(new Tx(addedTx, 8778)).Should().BeFalse();
        }

        [TestCategory("Tx")]
        [TestMethod, TestCategory("Unit Test")]
        public void ConcurrencyTokenCheckVisibilitiesMatchesResolveVisbilites()
        {
            var addedTx = ((ulong)45).EmbedStatus(TxStatus.Committed);
            var token1 = new ConcurrencyToken(8776, 8770);
            var tx = new Tx(addedTx, 8778);
            token1.CheckVisibilities(tx)
                .Should()
                .Be(tx.ResolveVisibilities(8776));
        }
    }
}
